package com.example.demo.proxy.impl;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.example.demo.model.dto.*;
import com.example.demo.model.req.*;
import com.example.demo.proxy.AvataProxy;
import com.example.demo.util.AvataProperties;
import com.example.demo.util.AvataUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import javax.annotation.Resource;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;

/**
 * @author enlai.wang 16621134798
 * @date: 2022/5/26
 */
@Slf4j
@Service
@AllArgsConstructor
public class AvataProxyImpl implements AvataProxy {

    @Autowired
    private AvataProperties avataProperties;
    @Resource
    private RestTemplate restTemplate;

    private static final String CREATE_ACCOUNT_URL = "/v1beta1/account";

    private static final String NFT_CLASS_LIST_QUERY_LIST = "/v1beta1/nft/classes";

    private static final String NFT_PUBLISH_URL_PRE = "/v1beta1/nft/nfts/";

    private static final String DEAL_RESULT_QUERY_URL_PRE = "/v1beta1/tx/";

    private static final String NFT_TRANSFER_URL_PRE = "/v1beta1/nft/nft-transfers/";

    private static final String NFT_CREATE_URL = "/v1beta1/nft/classes";

    private static final String NFT_DESTORY_URL_PRE = "/v1beta1/nft/nfts/";
    @Override
    public CreateAccountDTO createAccount(String name, String operationId) {
        if (StringUtils.isBlank(name)) {
            throw new RuntimeException("缺少必传参数name");
        }
        if (StringUtils.isBlank(operationId)) {
            throw new RuntimeException("缺少必传参数operationId");
        }
        Long currentTime  = System.currentTimeMillis();
        // 请求body
        Map<String, Object> body = new HashMap<>();
        body.put("name", name);
        body.put("operation_id", operationId);
        // 验签
        String signature = AvataUtils.signRequest(CREATE_ACCOUNT_URL, null, body, currentTime, avataProperties.getApiSecret());
        // 请求体
        HttpHeaders headers = getHttpHeader(signature, currentTime);
        headers.setContentType(MediaType.parseMediaType("application/json"));

        HttpEntity<String> httpEntity = new HttpEntity<>(JSONObject.toJSONString(body), headers);

        String url = avataProperties.getAvataUrl() + CREATE_ACCOUNT_URL;
        log.info("文昌链接口请求地址"+url);
        try {
            // 接口调用
            ParameterizedTypeReference<Message<CreateAccountDTO>> typeReference = new ParameterizedTypeReference<Message<CreateAccountDTO>>() {
            };
            ResponseEntity<Message<CreateAccountDTO>> responseEntity;
            responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, typeReference);

            Message<CreateAccountDTO> message = responseEntity.getBody();

            CreateAccountDTO createAccountDTO = message.getData();
            // 成功
            log.info("创建链账户成功:" + createAccountDTO);

            return createAccountDTO;
        } catch (Exception e) {
            // 非200都会抛异常
            // 处理异常
            log.info("非200处理链账户创建失败:"+name+":"+operationId);
            e.printStackTrace();
            throw new RuntimeException("链账户创建失败");
        }
    }

    @Override
    public NFTClassListDTO queryNFTClassList(NFTClassListReq nftClassListReq) {
        Map<String, Object> query = null == nftClassListReq ? new HashMap<>() :
                JSON.parseObject(JSON.toJSONString(nftClassListReq), new TypeReference<Map<String, Object>>() {
                });
        Long currentTime = System.currentTimeMillis();
        // 验签
        String signature = AvataUtils.signRequest(NFT_CLASS_LIST_QUERY_LIST, query, null, currentTime, avataProperties.getApiSecret());

        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(avataProperties.getAvataUrl() + NFT_CLASS_LIST_QUERY_LIST);
        for (Map.Entry<String, Object> entry : query.entrySet()) {
            builder.queryParam(entry.getKey(), entry.getValue());
        }

        HttpHeaders headers = getHttpHeader(signature, currentTime);

        try {
            // 接口调用
            ParameterizedTypeReference<Message<NFTClassListDTO>> typeReference = new ParameterizedTypeReference<Message<NFTClassListDTO>>() {
            };
            ResponseEntity<Message<NFTClassListDTO>> responseEntity = restTemplate.exchange(new URI(builder.toUriString()), HttpMethod.GET, new HttpEntity<>(headers), typeReference);

            Message<NFTClassListDTO> message = responseEntity.getBody();
            NFTClassListDTO nftClassListDTO = message.getData();

            if (null != nftClassListDTO && null != nftClassListDTO.getClasses() && nftClassListDTO.getClasses().size() != 0) {
                // 遍历处理类别列表
                for (NFTClassDTO nftClassDTO : nftClassListDTO.getClasses()) {

                }
            }

            log.info("NFT类别查询结果：" + nftClassListDTO);

            return nftClassListDTO;

        } catch (Exception e) {
            // 非200都会抛异常
            // 处理异常
            log.info("非200查询NFT类别失败:"+nftClassListReq);
            e.printStackTrace();
            throw new RuntimeException("查询NFT类别失败");
        }
    }

    @Override
    public NFTOperateDTO publishNFT(NFTPublishReq nftPublishReq) {
        if (null == nftPublishReq) {
            log.info("发行NFT失败，缺少参数:"+ null);
            throw new RuntimeException("发行NFT失败，缺少参数");
        }
        if (null == nftPublishReq.getClass_id()) {
            log.info("发行NFT失败，缺少参数class_id:"+nftPublishReq);
            throw new RuntimeException("发行NFT失败，缺少参数class_id");
        }
        if (null == nftPublishReq.getName()) {
            log.info("发行NFT失败，缺少参数name:"+nftPublishReq);
            throw new RuntimeException("发行NFT失败，缺少参数name");
        }
        if (null == nftPublishReq.getOperation_id()) {
            log.info("发行NFT失败，缺少参数operation_id:"+nftPublishReq);
//            throw new RuntimeException("发行NFT失败，缺少参数operation_id");
        }

        String path = NFT_PUBLISH_URL_PRE + nftPublishReq.getClass_id();

        Map<String, Object> body = JSON.parseObject(JSON.toJSONString(nftPublishReq), new TypeReference<Map<String, Object>>() {
        });

        body.remove("class_id");

        Long currentTime = System.currentTimeMillis();
        // 验签
        String signature = AvataUtils.signRequest(path, null, body, currentTime, avataProperties.getApiSecret());
        // 请求体
        HttpHeaders headers = getHttpHeader(signature, currentTime);
        headers.setContentType(MediaType.parseMediaType("application/json"));

        HttpEntity<String> httpEntity = new HttpEntity<>(JSONObject.toJSONString(body), headers);

        String url = avataProperties.getAvataUrl() + path;
        try {
            // 接口调用
            ParameterizedTypeReference<Message<NFTOperateDTO>> typeReference = new ParameterizedTypeReference<Message<NFTOperateDTO>>() {
            };
            ResponseEntity<Message<NFTOperateDTO>> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, typeReference);

            Message<NFTOperateDTO> message = responseEntity.getBody();

            NFTOperateDTO nftOperateDTO = message.getData();
            // 成功
            log.info("发行NFT成功:" + nftOperateDTO);

            return nftOperateDTO;
        } catch (Exception e) {
            // 非200都会抛异常
            // 处理异常
            log.info("非200发行NFT失败:参数为："+nftPublishReq);
            e.printStackTrace();
            throw new RuntimeException("发行NFT失败");
        }
    }

    @Override
    public DealResultDTO queryDealResult(String taskId) {
        if (StringUtils.isBlank(taskId)) {
            log.info("上链交易结果查询失败，缺少参数taskId:"+taskId);
            throw new RuntimeException("上链交易结果查询失败，缺少参数taskId");
        }

        String path = DEAL_RESULT_QUERY_URL_PRE + taskId;

        Long currentTime = System.currentTimeMillis();
        // 验签
        String signature = AvataUtils.signRequest(path, null, null, currentTime, avataProperties.getApiSecret());

        HttpHeaders headers = getHttpHeader(signature, currentTime);

        try {
            // 接口调用
            ParameterizedTypeReference<Message<DealResultDTO>> typeReference = new ParameterizedTypeReference<Message<DealResultDTO>>() {
            };
            ResponseEntity<Message<DealResultDTO>> responseEntity = restTemplate.exchange(avataProperties.getAvataUrl() + path, HttpMethod.GET, new HttpEntity<>(headers), typeReference);

            Message<DealResultDTO> message = responseEntity.getBody();
            DealResultDTO dealResultDTO = message.getData();
            log.info("上链交易结果查询：" + dealResultDTO);
            return dealResultDTO;

        } catch (Exception e) {
            // 非200都会抛异常
            // 处理异常
            log.info("非200上链交易结果查询失败");
            e.printStackTrace();
            throw new RuntimeException("上链交易结果查询失败");
        }
    }

    @Override
    public NFTOperateDTO transferNFT(NFTTransferReq nftTransferReq) {
        if (null == nftTransferReq) {
            log.info("转让NFT失败，缺少参数:"+nftTransferReq);
            throw new RuntimeException("转让NFT失败，缺少参数");
        }
        if (null == nftTransferReq.getClass_id()) {
            log.info("转让NFT失败，缺少参数class_id:"+nftTransferReq);
            throw new RuntimeException("转让NFT失败，缺少参数class_id");
        }
        if (null == nftTransferReq.getOwner()) {
            log.info("转让NFT失败，缺少参数owner:"+nftTransferReq);
            throw new RuntimeException("转让NFT失败，缺少参数owner");
        }
        if (null == nftTransferReq.getNft_id()) {
            log.info("转让NFT失败，缺少参数nft_id:"+nftTransferReq);
            throw new RuntimeException("转让NFT失败，缺少参数nft_id");
        }
        if (null == nftTransferReq.getRecipient()) {
            log.info("转让NFT失败，缺少参数recipient:"+nftTransferReq);
            throw new RuntimeException("转让NFT失败，缺少参数recipient");
        }
        if (null == nftTransferReq.getOperation_id()) {
            log.info("转让NFT失败，缺少参数operation_id:"+nftTransferReq);
            throw new RuntimeException("转让NFT失败，缺少参数operation_id");
        }

        String path = NFT_TRANSFER_URL_PRE + nftTransferReq.getClass_id() + "/" + nftTransferReq.getOwner() + "/" + nftTransferReq.getNft_id();

        Map<String, Object> body = JSON.parseObject(JSON.toJSONString(nftTransferReq), new TypeReference<Map<String, Object>>() {
        });

        body.remove("class_id");
        body.remove("owner");
        body.remove("nft_id");

        Long currentTime = System.currentTimeMillis();
        // 验签
        String signature = AvataUtils.signRequest(path, null, body, currentTime, avataProperties.getApiSecret());
        // 请求体
        HttpHeaders headers = getHttpHeader(signature, currentTime);
        headers.setContentType(MediaType.parseMediaType("application/json"));

        HttpEntity<String> httpEntity = new HttpEntity<>(JSONObject.toJSONString(body), headers);

        String url = avataProperties.getAvataUrl() + path;
        try {
            // 接口调用
            ParameterizedTypeReference<Message<NFTOperateDTO>> typeReference = new ParameterizedTypeReference<Message<NFTOperateDTO>>() {
            };
            ResponseEntity<Message<NFTOperateDTO>> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, typeReference);

            Message<NFTOperateDTO> message = responseEntity.getBody();

            NFTOperateDTO nftOperateDTO = message.getData();
            // 成功
            log.info("转让NFT成功:" + nftOperateDTO);

            return nftOperateDTO;
        } catch (Exception e) {
            // 非200都会抛异常
            // 处理异常
            log.info("转让NFT失败"+nftTransferReq);
            e.printStackTrace();
            throw new RuntimeException("转让NFT失败");
        }
    }

    @Override
    public NFTOperateDTO createNFT(NFTCreateReq nftCreateReq) {
        if (null == nftCreateReq) {
            log.info("创建NFT失败，缺少参数："+ null);
            throw new RuntimeException("创建NFT失败，缺少参数");
        }
        if (null == nftCreateReq.getName()) {
            log.info("创建NFT失败，缺少参数name："+nftCreateReq);
            throw new RuntimeException("创建NFT失败，缺少参数name");
        }
        if (null == nftCreateReq.getOwner()) {
            log.info("创建NFT失败，缺少参数owner："+nftCreateReq);
            throw new RuntimeException("创建NFT失败，缺少参数owner");
        }
        if (null == nftCreateReq.getOperation_id()) {
            log.info("创建NFT失败，缺少参数operation_id："+nftCreateReq);
            throw new RuntimeException("创建NFT失败，缺少参数operation_id");
        }

        Map<String, Object> body = JSON.parseObject(JSON.toJSONString(nftCreateReq), new TypeReference<Map<String, Object>>() {
        });


        Long currentTime = System.currentTimeMillis();
        // 验签
        String signature = AvataUtils.signRequest(NFT_CREATE_URL, null, body, currentTime, avataProperties.getApiSecret());
        // 请求体
        HttpHeaders headers = getHttpHeader(signature, currentTime);
        headers.setContentType(MediaType.parseMediaType("application/json"));

        HttpEntity<String> httpEntity = new HttpEntity<>(JSONObject.toJSONString(body), headers);

        String url = avataProperties.getAvataUrl() + NFT_CREATE_URL;
        try {
            // 接口调用
            ParameterizedTypeReference<Message<NFTOperateDTO>> typeReference = new ParameterizedTypeReference<Message<NFTOperateDTO>>() {
            };
            ResponseEntity<Message<NFTOperateDTO>> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, typeReference);

            Message<NFTOperateDTO> message = responseEntity.getBody();

            NFTOperateDTO nftOperateDTO = message.getData();
            // 成功
            log.info("创建NFT成功:" + nftOperateDTO);

            return nftOperateDTO;
        } catch (Exception e) {
            // 非200都会抛异常
            // 处理异常
            log.info("创建NFT失败"+nftCreateReq);
            e.printStackTrace();
            throw new RuntimeException("创建NFT失败");
        }
    }



    @Override
    public NFTOperateDTO destroyNFT(NFTDestoryReq nftDestoryReq) {
        if (null == nftDestoryReq) {
            log.info("销毁NFT失败，缺少参数:"+nftDestoryReq);
            throw new RuntimeException("销毁NFT失败，缺少参数");
        }
        if (null == nftDestoryReq.getClass_id()) {
            log.info("销毁NFT失败，缺少参数class_id:"+nftDestoryReq);
            throw new RuntimeException("销毁NFT失败，缺少参数class_id");
        }
        if (null == nftDestoryReq.getOwner()) {
            log.info("销毁NFT失败，缺少参数owner:"+nftDestoryReq);
            throw new RuntimeException("销毁NFT失败，缺少参数owner");
        }
        if (null == nftDestoryReq.getNft_id()) {
            log.info("销毁NFT失败，缺少参数nft_id:"+nftDestoryReq);
            throw new RuntimeException("销毁NFT失败，缺少参数nft_id");
        }
        if (null == nftDestoryReq.getOperation_id()) {
            log.info("销毁NFT失败，缺少参数operation_id:"+nftDestoryReq);
            throw new RuntimeException("销毁NFT失败，缺少参数operation_id");
        }

        String path = NFT_DESTORY_URL_PRE + nftDestoryReq.getClass_id() + "/" + nftDestoryReq.getOwner() + "/" + nftDestoryReq.getNft_id();

        Map<String, Object> body = JSON.parseObject(JSON.toJSONString(nftDestoryReq), new TypeReference<Map<String, Object>>() {
        });

        body.remove("class_id");
        body.remove("owner");
        body.remove("nft_id");

        Long currentTime = System.currentTimeMillis();
        // 验签
        String signature = AvataUtils.signRequest(path, null, body, currentTime, avataProperties.getApiSecret());
        // 请求体
        HttpHeaders headers = getHttpHeader(signature, currentTime);
        headers.setContentType(MediaType.parseMediaType("application/json"));

        HttpEntity<String> httpEntity = new HttpEntity<>(JSONObject.toJSONString(body), headers);

        String url = avataProperties.getAvataUrl() + path;
        try {
            // 接口调用
            ParameterizedTypeReference<Message<NFTOperateDTO>> typeReference = new ParameterizedTypeReference<Message<NFTOperateDTO>>() {
            };
            ResponseEntity<Message<NFTOperateDTO>> responseEntity = restTemplate.exchange(url, HttpMethod.DELETE, httpEntity, typeReference);

            Message<NFTOperateDTO> message = responseEntity.getBody();

            NFTOperateDTO nftOperateDTO = message.getData();
            // 成功
            log.info("销毁NFT成功:" + nftOperateDTO);

            return nftOperateDTO;
        } catch (Exception e) {
            // 非200都会抛异常
            // 处理异常
            log.info("销毁NFT失败"+nftDestoryReq);
            e.printStackTrace();
            throw new RuntimeException("销毁NFT失败");
        }
    }

    HttpHeaders getHttpHeader(String signature, Long timestamp) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("X-Api-Key", avataProperties.getApiKey());
        headers.set("X-Timestamp", timestamp.toString());
        headers.set("X-Signature", signature);

        return headers;
    }
}
